/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package pianostadui_javav2;

import java.util.*;





/*

L'esercizio di oggi consiste nella scrittura di una classe PianoDiStudi che
permetta di gestire il piano di studi di uno studente di informatica.

Un piano di studi e' semplicemente una lista di stringhe dove ogni stringa e'
il nome di un esame. A rendere interessante l'esercizio e' il fatto che ogni
esame puo' avere dei prerequisiti nel senso che per poter dare un certo esame
puo' essere necessario aver sostenuto uno o piu' altri esami (ad esempio,
programmazione 1, programmazione 2, e asd1 possono essere prerequisiti per
asd2).

Inoltre ogni esame ha un valore in crediti ed e' definito un insieme
di esami che devono essere obbligatoriamente inseriti in un piano di studi
(questi concetti immagino vi siano familiari).

*/



// La classe PianoDiStudi deve contentere i seguenti metodi, piu'
// eventuali altri che ritenete necessari


class PianoDiStudi{
   List<String> esami;
   static Set<String> obbligatori= new HashSet<String>();
   static Map<String,List<String>> prerequisiti = new HashMap<String,List<String>>();
   static Map<String,Integer> crediti = new HashMap<String,Integer>();

   static int rendi_obbligatori(List<String> l){
       obbligatori.addAll(l);
       return obbligatori.size();
   }



// Metodo di classe: rendi_obbligatori(Lista)
// aggiunge all'elenco degli esami obbligatori tutti gli esami in Lista
// Restituisce il nuovo numero di esami obbligatori




  // Metodo di classe: aggiungi_prerequisito(Nome,Lista)
  // stabilisce che gli esami in Lista sono un prerequisito per l'esame Nome
  // eventualmente aggiungendoli ad altri prerequisiti gia' fissati per Nome
  // Restituisce il nuovo numero di esami che sono prerequisti per Nome

   static int aggiungi_prerequisito(String nome, List<String> lista) {
    List<String> pieno = prerequisiti.get(nome);
    if (pieno==null)
        pieno = new ArrayList<String>(lista);
    else
        pieno.addAll(lista);
    prerequisiti.put(nome, pieno);
    return pieno.size();
   }

/*
  // Metodo di classe: assegna_crediti(E,C)
  // stabilisce che l'esame E ha un valore di C crediti
  // se ad un esame non viene assegnato un numero di crediti si assume
  // che abbia un valore di 5 crediti
*/
  static void assegna_crediti(String esame, int c) {
      crediti.put(esame, c);
  }

  // ----------------- fine metodi di classe --------------------


  // costruttore
  PianoDiStudi() {
      esami = new ArrayList<String>();
  }

  // aggiunge_esame(X)
  // aggiunge l'esame X al piano di studi self, ma solo se non e' gia' in self
  // e se tutti i prerequisiti di X sono gia' in self.
  // Restituisce true se l'esame viene aggiunto false altrimenti
  boolean aggiungi_esame(String e) {
     if (esami.contains(e)){
         return false;
      }
     //System.out.println("----" + prerequisiti.get(e)+ "----");
     if (prerequisiti.get(e)!=null){
     List<String> tmp= new ArrayList<String>(prerequisiti.get(e));
     for (String i:tmp){
         if (!esami.contains(i)){
             return false;
         }
     }}
    return esami.add(e);
  
  }


  // non cancellare questo metodo: serve per gli esperimenti
  int aggiungi_esami(List<String> lista) {
    int tot = 0;
    for(String e : lista) {
        if(aggiungi_esame(e))
            tot +=1;
    }
    return tot;
  }




  // elimina_esame(X)
  // elimina l'esame X da self ma solo se e' presente e non e'
  // un prerequisito per un altro esame presente in self.
  // Restituisce true se l'esame viene eliminato false altrimenti
  boolean elimina_esame(String e) {
      if(!esami.contains(e))
		  return false;
	  for(String i: esami)
		  if (prerequisiti.get(i)!=null && prerequisiti.get(i).contains(e))
			  return false;
	  esami.remove(e);
	  return true;
  }


  // valido(N)
  // restituisce true se nel piano di studi sono stati inclusi tutti gli
  // esami obbligatori e il numero totale di crediti e' >= N
  boolean valido(int n) {
      for(String i:obbligatori){
          if (!esami.contains(i))
              return false;
      }
      int tot=0;
      for (String i:esami){
          if (crediti.containsKey(i))
              tot+=crediti.get(i);
      }
      if (tot>=n)
      return true;
      return false;
  }

  // crediti_comuni(P)
  // calcola e restituisce quanti sono i crediti in comune
  // fra i piani di studio self e P
  // in altre parole restituisce il valore, in crediti, degli esami che
  // compaiono sia in self che in P
  int crediti_comuni(PianoDiStudi p) {
      int tot=0;
      for(String i:p.esami){
          if (esami.contains(i)){
              if (crediti.get(i)==null){
                  tot+=5;
                  //System.out.println("-----"+ tot +"-----");
              }
              else{
              tot+=crediti.get(i);
              }
          }
      }
      return tot;
  }


/*
  Infine un metodo di classe un po' piu' complicato degli altri (ma comunque
  realizzabile con una ventina di righe di codice):

  Metodo di classe: riordina(Lista_esami)
  riordina gli esami in Lista_esami in modo che non siano violati prerequisti.
  Il metodo deve restituire una lista di esami Lista_ordinata tale che
     * Lista_ordinata contiene gli stessi esami di Lista_esami
     * se gli esami e1 ed e2 compaiono in Lista_esami e l'esame e1 e'
        un prerequisto per l'esame e2, allora in Lista_ordinata
        l'esame e1 deve comparire prima di e2.
  Per il calcolo di Lista_ordinata potete utilizzare il seguente algoritmo:
   1. Porre Lista_ordinata = [], Visti=[], Coda=[]
   2. copiare gli esami di Lista_esami in Coda
   3. fino a quando Coda non e' vuota
   3a.   sia E il primo esame in Coda
   3b.   se E e' contenuto in Lista_ordinata cancella E da Coda
   3c.   altrimenti se E e' contenuto in Visti mettilo in fondo a
            Lista_ordinata e cancella E da Coda
   3d.   altrimenti metti in cima a Coda tutti gli esami da cui E dipende
           (senza cancellare E) e metti E in Visti
   4. cancellare da Lista_ordinata gli esami che non sono in Lista_esami
   5. restituire Lista_ordinata
*/

  static List<String> riordina(List<String> lista_es) {

      List<String> lista_ordinata = new ArrayList<String>();
      List<String> visti = new ArrayList<String>();
      List<String> coda = new ArrayList<String>();
      coda.addAll(lista_es);
      
      while (!coda.isEmpty()){
          String e=coda.get(0);
         //if (lista_ordinata != null){
              if (lista_ordinata.contains(e)){
                coda.remove(e);
          }
               else if(visti.contains(e))
             {
              lista_ordinata.add(e);
              coda.remove(e);
            }
              else {
                visti.add(e);
                if (prerequisiti.containsKey(e))
                    coda.addAll(0,prerequisiti.get(e));
                  // inserisce p in cima a coda
             }
      }
      lista_ordinata.retainAll(lista_es);
      return lista_ordinata;
  }

} // fine classe


/*
 Per testare le vostre classi potete usare la classe main() riportata qui sotto
 che dovrebbe generare un output come quello riportato in fondo a questo file.
 */

public class Main {

  public static void main(String[] args) {

    List<String> tmp = new ArrayList<String>();
    PianoDiStudi.assegna_crediti("algebra", 4);
    PianoDiStudi.assegna_crediti("analisi", 6);
    PianoDiStudi.assegna_crediti("prog1", 10);
    PianoDiStudi.assegna_crediti("reti2", 8);
    PianoDiStudi.assegna_crediti("basidati1", 9);
    PianoDiStudi.assegna_crediti("sisop1", 8);

    System.out.println( "---------- obbligatori ------------");
    tmp.add("algebra"); tmp.add("analisi");
    System.out.println( PianoDiStudi.rendi_obbligatori(tmp));
    tmp.clear(); tmp.add("analisi"); tmp.add("prog2"); tmp.add("arch1");
    tmp.add("asd1"); tmp.add("oop"); tmp.add("reti1");
    System.out.println( PianoDiStudi.rendi_obbligatori(tmp));

    System.out.println( "---------- prerequisiti ------------");
    tmp.clear(); tmp.add("prog1");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("prog2",tmp));
    tmp.clear(); tmp.add("asd1");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("asd2",tmp));
    tmp.clear(); tmp.add("algebra"); tmp.add("analisi");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("asd1",tmp));
    tmp.clear(); tmp.add("prog2"); tmp.add("asd2");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("oop",tmp));
    tmp.clear(); tmp.add("reti1"); tmp.add("arch1");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("reti2",tmp));
    tmp.clear(); tmp.add("reti2");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("reti3",tmp));
    tmp.clear(); tmp.add("prog2"); tmp.add("prog1");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("asd2",tmp));
    tmp.clear(); tmp.add("asd1");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("oop",tmp));
    tmp.clear(); tmp.add("prog1");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("reti1",tmp));

    // ---- crea due istanze della classe PianoDiStudi
    PianoDiStudi p1 = new PianoDiStudi();
    PianoDiStudi p2 = new PianoDiStudi();

    System.out.println( "---------- aggiunge esami ------------");
    System.out.println( p1.aggiungi_esame("prog2"));
    tmp.clear(); tmp.add("analisi"); tmp.add("algebra"); tmp.add("asd1");
    tmp.add("prog1"); tmp.add("prog2"); tmp.add("asd2");
    //System.out.println(tmp);
    System.out.println( p1.aggiungi_esami(tmp));
    System.out.println( p1.aggiungi_esame("asd2"));
    System.out.println( p1.aggiungi_esame("prog2"));
    System.out.println( p1.aggiungi_esame("reti3"));
    System.out.println( p1.aggiungi_esame("reti1"));


    tmp.clear(); tmp.add("prog1"); tmp.add("prog2"); tmp.add("oop");
    System.out.println( p1.aggiungi_esami(tmp));
    tmp.clear(); tmp.add("reti1"); tmp.add("basidati1"); tmp.add("reti2");
    System.out.println( p1.aggiungi_esami(tmp));
    System.out.println( "---------- test valido? -------------");
    System.out.println( p1.valido(30));
    System.out.println( p1.aggiungi_esame("arch1"));
    System.out.println( p1.valido(30));
    System.out.println( p1.valido(80));
    System.out.println( p2.valido(-1));


    System.out.println( "---------- elimina esami ------------");
    System.out.println( p1.elimina_esame("analisi"));
    System.out.println( p1.elimina_esame("reti1"));
    System.out.println( p1.elimina_esame("economia1"));
    System.out.println( p1.elimina_esame("basidati1"));
    System.out.println( p1.elimina_esame("reti2"));
    System.out.println( p1.elimina_esame("reti1"));
    System.out.println( p1.valido(30));
    System.out.println( p1.aggiungi_esame("reti1"));
    System.out.println( p1.valido(30));

    System.out.println( "---------- confronta piani di studio ------------");
    System.out.println( p1.crediti_comuni(p2));

    tmp.clear(); tmp.add("analisi"); tmp.add("algebra"); tmp.add("asd1");
    tmp.add("prog1"); tmp.add("reti2"); tmp.add("arch1");
    System.out.println( p2.aggiungi_esami(tmp));
    tmp.clear(); tmp.add("sisop1"); tmp.add("reti3"); tmp.add("analisi");
    System.out.println( p2.aggiungi_esami(tmp));
    System.out.println( p1.crediti_comuni(p2));
    System.out.println( p1.crediti_comuni(p1));
    System.out.println( p2.crediti_comuni(p2));

    System.out.println( "------------- riordinamento esami ---------------");
    tmp.clear(); tmp.add("prog2"); tmp.add("asd1"); tmp.add("algebra");
    System.out.println( PianoDiStudi.riordina(tmp));
    System.out.println( "----");
    //PianoDiStudi.status()
    tmp.clear(); tmp.add("analisi"); tmp.add("reti3"); tmp.add("ret1");
    tmp.add("reti2"); tmp.add("algebra");
    System.out.println( PianoDiStudi.riordina(tmp));
    System.out.println( "----");
    tmp.clear(); tmp.add("reti3"); tmp.add("oop");
    tmp.add("analisi"); tmp.add("prog1");
    System.out.println( PianoDiStudi.riordina(tmp));
    System.out.println( "----");

    tmp.clear(); tmp.add("reti2"); tmp.add("asd2"); tmp.add("prog2");
    tmp.add("oop"); tmp.add("prog1");
    tmp.add("asd1"); tmp.add("oop"); tmp.add("reti1");
    System.out.println( PianoDiStudi.riordina(tmp));
    System.out.println( "----");

    tmp.clear(); tmp.add("asd2");
    System.out.println( PianoDiStudi.aggiungi_prerequisito("reti2",tmp) );
    System.out.println( "----");

    tmp.clear(); tmp.add("reti2"); tmp.add("asd2"); tmp.add("prog2");
    tmp.add("oop"); tmp.add("prog1");
    tmp.add("asd1"); tmp.add("oop"); tmp.add("reti1");
    System.out.println( PianoDiStudi.riordina(tmp));

    System.out.println( "------------- fine ---------------");
  }
}